#pragma once
#include <climits>
#include <cstddef>
#include <stdint.h>
#include <utility>
template <class T, size_t BlockSize = 4096>
class MemoryPool
{
public:
    typedef T *pointer;
    template <class U>
    struct rebind
    {
        typedef MemoryPool<U> other;
    };
    MemoryPool() noexcept
    {
        curBlock_ = curSlot_ = lastSlot_ = freeSlots_ = nullptr;
    }

    ~MemoryPool() noexcept
    {
        slot_pointer_ cur = curBlock_;
        while (cur)
        {
            slot_pointer_ prev = cur->next;
            operator delete(reinterpret_cast<void *>(cur));
            cur = prev;
        }
    }

    pointer allocate(size_t n = 1, const T *hint = nullptr)
    {
        if (freeSlots_)
        {
            pointer result = reinterpret_cast<pointer>(freeSlots_);
            freeSlots_ = freeSlots_->next;
            return result;
        }
        else
        {
            if (curSlot_ >= lastSlot_)
            {
                data_pointer_ newBlock = reinterpret_cast<data_pointer_>(operator new(BlockSize));
                reinterpret_cast<slot_pointer_>(newBlock)->next = curBlock_;
                curBlock_ = reinterpret_cast<slot_pointer_>(newBlock);
                data_pointer_ body = newBlock + sizeof(slot_pointer_);
                uintptr_t res = reinterpret_cast<uintptr_t>(body);
                size_t bodyPadding = (alignof(slot_type_) - res) % alignof(slot_type_);
                curSlot_ = reinterpret_cast<slot_pointer_>(body + bodyPadding);
                lastSlot_ = reinterpret_cast<slot_pointer_>(newBlock + BlockSize - sizeof(slot_type_) + 1);
            }
            return reinterpret_cast<pointer>(curSlot_++);
        }
    }

    void deallocate(pointer p, size_t n = 1)
    {
        if (p)
        {
            reinterpret_cast<slot_pointer_>(p)->next = freeSlots_;
            freeSlots_ = reinterpret_cast<slot_pointer_>(p);
        }
    }

    template <typename U, typename... Args>
    void construct(U *p, Args &&... args)
    {
        new (p) U(std::forward<Args>(args)...);
    }

    template <class U>
    void destroy(U *p)
    {
        p->~U();
    }

private:
    union Slot_ {
        T element;
        Slot_ *next;
    };
    typedef Slot_ slot_type_;
    typedef char *data_pointer_;
    typedef Slot_ *slot_pointer_;
    slot_pointer_ curBlock_;
    slot_pointer_ curSlot_;
    slot_pointer_ lastSlot_;
    slot_pointer_ freeSlots_;
    static_assert(BlockSize >= 2 * sizeof(slot_type_), "BlockSize Too Small");
};
